<?php
/**
 * Created by PhpStorm.
 * User: geyouwen
 * Date: 16/10/24
 * Time: 下午4:32
 */

namespace App\Helper\Mongo;


class Mongo
{

    public $mongo;
    static $instance;
    private $db, $collection;

    //构造函数必须私有 防止外部 new操作
    private function __construct($db, $col)
    {
        // eg: mongodb://root:password@host:port
        $server =  'mongodb://';
        $server =  'mongodb://192.168.1.17:27017';
//        if(C('MONGODB_CONF.USER_NAME') && C('MONGODB_CONF.PASSWORD'))
//        {
//            $server .=   C('MONGODB_CONF.USER_NAME').':'.C('MONGODB_CONF.PASSWORD').'@';
//        }
//        $server .= C('MONGODB_CONF.HOST').':'.C('MONGODB_CONF.PORT');
        $this->mongo = new \MongoClient($server);
        $this->db = $this->mongo->$db;
        $this->collection = $this->db->$col;
    }

    //必须私有 禁止clone
    private function __clone(){

    }

    /**
     * 单例模式 实例化
     * @param $db
     * @param $col
     * @return Mongo
     */
   public static function getInstance($db, $col)
    {
        if(!self::$instance instanceof self)
        {
            self::$instance = new self($db, $col);
        }
        return self::$instance;
    }

    /**
     * 插入单条
     * @param $data
     * @return string
     */
    public function insert($data)
    {
        if(!$data) {
            return '插入数据不能为空';
        }
        try{
            $this->collection->insert($data);
        } catch(\Exception $e)
        {
           return $e->getMessage();
        }
    }

    /**
     * 插入多条
     * @param $data
     * @return string
     */
    public function insertAll($data)
    {
        if(!$data) {
            return '插入数据不能为空';
        }
        try{
            foreach($data as $key => $val)
            {
                $this->collection->insert($val);
            }
        } catch(\Exception $e)
        {
            return $e->getMessage();
        }
    }

    /**
     * 查询单条
     * @param array $options
     * @return mixed
     */
    public function findOne($options = array())
    {
        $options = self::_buildOption($options);
        $data =  $options ? $this->collection->findOne($options) :$this->collection->findOne();
        return $data;
    }


    /**
     * 查询多条
     * @param array $options
     * @param array $fields
     * @param array $order eg: array(‘age’=>-1,’type’=>1) ##1表示升序 -1表示降序,参数的先后影响排序顺序
     * @param string $limit
     * @return array
     */
    public function select($options = array(), $fields = array(), $order = array(), $limit = '', $skip = 0)
    {
        $options = self::_buildOption($options);
        $list = $options ? $this->collection->find($options) : $this->collection->find();
        if($order)
        {
            foreach($order as $k => $v)
            {
                $order[$k] = $v == 'desc' ? -1 : 1;
            }
           $list = $list->sort($order);
        }
        if($limit)
        {
            $list = $list->limit($limit);
        }
        if($fields && $fields!= '*')
        {
            $fields = explode(',',$fields);
            foreach($fields as  $key => $val)
            {
                $columns[$val] = true;
            }
            $list = $list->fields($columns);
        }
        if($skip)
        {
            $list = $list->skip(($skip-1)*$limit);
        }
        $temp = array();
        if($list)
        {
            foreach($list as $key => $val)
            {
                if(isset($val['_id'])) $val['_id'] = (string)$val['_id'];
                $temp[] = $val;
            }
        }
        return $temp;
    }

    /**
     * 统计总数
     * @param array $options
     * @return mixed
     */
    public function count($options = array())
    {
        $options = self::_buildOption($options);
        return $options ? $this->collection->count($options) : $this->collection->count();
    }

    //简单分页
    public function page($options = array(), $fields = '*', $page = 1, $page_size = 20, $order = array())
    {
        $options = self::_buildOption($options);
       $data  = self::select($options, $fields, $order, $page_size, $page);
       $count = self::count($options);
       $page_data = array(
           'record_count' => $count,
           'current'      => $page,
           'prev'         => ($page<=1) ? 1 : $page-1,
           'next'         => $page >= $count ? $count : $page+1,
           'page_size'    => $page_size,
           'page_count'   => ceil($count/$page_size),
       );
       return $result = array(
           'data'  => $data,
           'page'  => $page_data
       );
    }

    /**
     * 更新数据
     * @param array $options
     * @param $data
     * @return mixed
     */
    public function update($options, $data)
    {
        $options = self::_buildOption($options);
       return $this->collection->update($options, array('$set' => $data));
    }

    // 自增 自减
    /**
     * @param $options array
     * @param $field
     * @param int $step
     * @return mixed
     */
    public function inc($options, $field, $step = 1)
    {
        $options = self::_buildOption($options);
        return $this->collection->update($options, array('$inc' => array($field => $step)));
    }

    //关闭连接
    public function close()
    {
        $this->mongo->close();
    }

    /**
     * 切换collection
     * @param $new_col
     * @return mixed
     */
    public function switchCol($new_col)
    {
       return $this->collection = $this->db->$new_col;
    }

    /**
     * 删除节点
     * @param array $options
     * @param $field
     * @return mixed
     */
    public function rmFiled($options = array(), $field)
    {
        $options = self::_buildOption($options);
        return $this->collection->update($options, array('$unset' => $field));
    }

    /**
     * 删除文档
     * @param array $options
     * @return mixed
     */
    public function del($options = array())
    {
        $options = self::_buildOption($options);
        return $this->collection->remove($options);
    }

    //添加索引
    /**
     * @param $keys array('id'=>1)
     */
    public function addIndex($keys)
    {
       $this->collection->ensureIndex($keys);
    }

    /**
     * 删除索引
     * @param $key eg: array('id'=>1)
     */
    public function delIndex($key)
    {
        $this->collection->deleteIndex($key);
    }


    /**
     * 格式化查询条件  （模糊查询）
     * @param $options
     * @return array
     */
    private function _buildOption($options)
    {
        if($options && is_array($options))
        {
            foreach($options as $key => $val)
            {
                if(is_array($val) && isset($val['$like']))
                {
                    $options[$key] = new \MongoRegex('/'.$val['$like'].'/');
                }
            }
        }
        return $options;
    }

    // 执行命令
    public function command($query)
    {
        $this->db->runCommand($query);
    }


    // LBS服务 location based service
    public function distance($latitude, $query = array(), $num = false)
    {
        $options = array(
            'geoNear' => 'location',
            'near'    => $latitude,
            'distanceMultiplier' => 6378137,  //返回距离单位 m
            'spherical'          => true
        );
        if($num) $options['num'] = $num;
        if($query) $options['query'] = $this->_buildOption($query);
        $list = $this->db->command($options);
        return $list;
    }


}